#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include "stm32mp157_rcc.h"
#include "stm32mp157_gpio.h"

struct stm32mp157_led{
	rcc_t * rcc;
	gpio_t * gpioz;
	gpio_t * gpioe;
	gpio_t * gpiof;
};

struct stm32mp157_led * led;

ssize_t led_drv_write(struct file * filp, const char __user * buff, size_t size, loff_t * pos)
{
	int ret;
	int led_cmd;

    struct inode * node;
	int major;
	int minor;
	int * num;
	node = filp->f_path.dentry->d_inode;
	major = imajor(node);
	minor = iminor(node);
	printk("major=%d, minor=%d\n", major, minor);

	num = (int *)filp->private_data;
	printk("num=%d\n", *num);
	
	//把用户空间的数据拷贝到内核空间(内核空间不能直接访问用户空间)
	//返回值>0: 没有拷贝成功的个数，失败！
	ret = copy_from_user(&led_cmd, buff, size);
	if(ret > 0)
	{
		printk("copy_from_user error\n");
		return -EAGAIN;
	}
	if(led_cmd == 1)
	{
		led->gpioz->ODR |= (0x01 << 5) | (0x01 << 6) | (0x01 << 7);
		led->gpioe->ODR |= (0x01 << 8) | (0x01 << 10);
		led->gpiof->ODR |= (0x01 << 10);
	}
	else
	{
		led->gpioz->ODR &= ~((0x01 << 5) | (0x01 << 6) | (0x01 << 7));
		led->gpioe->ODR &= ~((0x01 << 8) | (0x01 << 10));
		led->gpiof->ODR &= ~(0x01 << 10);
	}
	return 0;
}
	
int led_drv_open(struct inode * node, struct file * filp)
{
	static int num = 100;
	filp->private_data = &num;
	printk("----------%s----------\n",__FUNCTION__);
	return 0;
}
	
int led_drv_release(struct inode * node, struct file * filp)
{
	printk("----------%s----------\n",__FUNCTION__);
	return 0;
}

const struct file_operations led_fops = {
	.write = led_drv_write,
	.open = led_drv_open,
	.release = led_drv_release,
};

struct miscdevice led_misc = {
	.minor = 199,
	.name = "led_drv",
	.fops = &led_fops,
};

static int __init led_drv_init(void)
{
	int ret;
	//0.创建全局的结构体对象
	led = kzalloc(sizeof(struct stm32mp157_led), GFP_KERNEL);
	if(!led)
	{
		printk("kzalloc error\n");
		return -ENOMEM;
	}
	
	//1. 注册杂项设备
	ret = misc_register(&led_misc);
	if(ret < 0)
	{
		printk("misc_register error\n");
		goto kzalloc_err;
	}
	
	//2. 硬件初始化
	//虚拟地址映射
	led->rcc = ioremap(RCC, sizeof(rcc_t));
	if(!led->rcc)
	{
		ret = -ENOMEM;
		printk("ioremap_rcc error\n");
		goto misc_register_err;
	}
	//开启锁相环
	led->rcc->PLL4CR |= (1 << 0);
	//判断PLL时钟是否稳定
	while(!(led->rcc->PLL4CR & (1 << 1)))
	{
	}
	//开启GPIOE和GPIOF时钟
	led->rcc->MP_AHB4ENSETR |= (1 << 4) | (1 << 5);

    //设置GPIOZ
	led->gpioz = ioremap(GPIOZ, sizeof(gpio_t));
	if(!led->gpioz)
	{
		ret = -ENOMEM;
		printk("ioremap_gpioz error\n");
		goto ioremap_rcc_err;
	}
	led->gpioz->MODER &= ~((0x03 << 10) | (0x03 << 12) | (0x03 << 14));
	led->gpioz->MODER |= (0x01 << 10) | (0x01 << 12) | (0x01 << 14);
	led->gpioz->OTYPER &= ~((0x01 << 5) | (0x01 << 6) | (0x01 << 7));
	led->gpioz->OSPEEDR &= ~((0x03 << 10) | (0x03 << 12) | (0x03 << 14));
	led->gpioz->ODR &= ~((0x01 << 5) | (0x01 << 6) | (0x01 << 7));
	
	led->gpioe = ioremap(GPIOE, sizeof(gpio_t));
	if(!led->gpioe)
	{
		ret = -ENOMEM;
		printk("ioremap_gpioe error\n");
		goto ioremap_gpioz_err;
	}
	led->gpioe->MODER &= ~((0x03 << 16) | (0x03 << 20));
	led->gpioe->MODER |= (0x01 << 16) | (0x01 << 20);
	led->gpioe->OTYPER &= ~((0x01 << 8) | (0x01 << 10));
	led->gpioe->OSPEEDR &= ~((0x03 << 16) | (0x03 << 20));
	led->gpioe->ODR &= ~((0x01 << 8) | (0x01 << 10));

	led->gpiof = ioremap(GPIOF, sizeof(gpio_t));
	if(!led->gpiof)
	{
		ret = -ENOMEM;
		printk("ioremap_gpiof error\n");
		goto ioremap_gpiof_err;
	}
	led->gpiof->MODER &= ~(0x03 << 20);
	led->gpiof->MODER |= (0x01 << 20);
	led->gpiof->OTYPER &= ~(0x01 << 10);
	led->gpiof->OSPEEDR &= ~(0x03 << 20);
	led->gpiof->ODR &= ~(0x01 << 10);
	return 0;

	ioremap_gpiof_err:
		iounmap(led->gpioe);
	ioremap_gpioz_err:
		iounmap(led->gpioz);
	ioremap_rcc_err:
		iounmap(led->rcc);
	misc_register_err:
		misc_deregister(&led_misc);
	kzalloc_err:
		kfree(led);

	return ret;
}

static void __exit led_drv_exit(void)
{
	iounmap(led->gpiof);
	iounmap(led->gpioe);
	iounmap(led->gpioz);
	iounmap(led->rcc);
	misc_deregister(&led_misc);
	kfree(led);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

